#include <stdlib.h>
#include <string.h>

#ifdef UNIX
# include <unistd.h>
#else
# include <direct.h>
#endif

#include "../fs/metafs.h"
#include "../fs/dirfs.h"
#include "../hle.h"

#include "__IoFileMgrForUser.h"

char *homebrewPath = NULL;
DirFileSystem fsdir;
SceUID *dirHandles = NULL;
u32 numDirHandles = 0;

void __IoSetPath(char *path)
{
    dfs_setBasePath(&fsdir, path);
}

void __IoInit()
{
    System fs;
    char *cwd, *path;
    u32 len;

    fs.system = &fsdir;
    dfs_init(&fsdir);
#ifdef UNIX
    cwd = getcwd(NULL, 0);
#else
    cwd = _getcwd(NULL, 0);
#endif
    len = strlen(cwd);
    path = malloc(len + 6);
    path[0] = '\0';

    strcpy(path, cwd);
    path[len + 0] = PTHSPRT;
    path[len + 1] = 'm';
    path[len + 2] = 's';
    path[len + 3] = '0';
    path[len + 4] = PTHSPRT;
    path[len + 5] = '\0';

    free(cwd);

    dfs_setBasePath(&fsdir, path);

    free(path);

    mfs_mount("ms0:", DIRFS, &fs);
    mfs_mount("fatms0:", DIRFS, &fs);
    mfs_mount("fatms:", DIRFS, &fs);
    mfs_chdir("ms0:");
    mfs_showFS();
}

void __IoShutdown()
{
    free(homebrewPath);
    while (numDirHandles)
    {
        SceUID id = dirHandles[0];
        DirListing *dir = ko_get(id);
        if (dir)
        {
            if (dir->nlisting != -1)
                mfs_freeDirListing(dir->name, dir->listing);
            mem_freeStr(dir->name);
            ko_free(id);
            __IoDelDirHandle(id);
        }
    }
}

void __IoGetStat(SceIoStat *stat, FileInfo info)
{
    s32 type, attr;

    memset(stat, 0xfe, sizeof(SceIoStat));
    stat->st_size = info.size;

    if (info.type & FILETYPE_DIRECTORY) {
        type = SCE_STM_FDIR;
        attr = TYPE_DIR;
    }
    else {
        type = SCE_STM_FREG;
        attr = TYPE_FILE;
    }

    stat->st_mode = 0777 | type;
    stat->st_attr = attr;
    stat->st_size = info.size;
    stat->st_private[0] = info.startSector;
}

u32 __IoClose(SceUID id)
{
    _log(INF, HLE, "Deferred IoClose(%d)", id);
    if (ko_get(id)) {
        ko_free(id);
        return 0;
    }
    else
        return SCE_ERROR_BAD_FILE_DESCRIPTOR;
}

void __IoAddDirHandle(SceUID id)
{
    if (dirHandles == NULL)
        dirHandles = malloc(sizeof(*dirHandles));
    else
        dirHandles = realloc(dirHandles, sizeof(*dirHandles) * (numDirHandles + 1));
    dirHandles[numDirHandles++] = id;
}

void __IoDelDirHandle(SceUID id)
{
    u32 i, j;
    for (i = 0; i < numDirHandles; i++)
    {
        if (dirHandles[i] == id)
        {
            numDirHandles--;
            for (j = i; j < numDirHandles; j++)
                dirHandles[j] = dirHandles[j + 1];
            if (numDirHandles == 0) {
                free(dirHandles);
                dirHandles = NULL;
            }
            else
                dirHandles = realloc(dirHandles, sizeof(*dirHandles) * numDirHandles);
            return;
        }
    }
}

